package com.sunb2b.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Version;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.wltea.analyzer.lucene.IKAnalyzer;
import org.wltea.analyzer.lucene.IKQueryParser;

import com.sunb2b.dao.CategoryDAO;
import com.sunb2b.dao.ProductDAO;
import com.sunb2b.dao.RegionDAO;
import com.sunb2b.entity.Category;
import com.sunb2b.entity.Member;
import com.sunb2b.entity.Product;
import com.sunb2b.entity.Region;
import com.sunb2b.pojo.SearchResult;
import com.sunb2b.util.B2B;
import com.sunb2b.web.form.SearchCriteria;

@Service
public class SearchEngine {

    public static final int STATUS_INDEXING = 0;
    public static final int STATUS_OK = 1;
    public int status = STATUS_OK;
    public static boolean initialized = false;
    private String indexDir;
    private FSDirectory directory;
    private IndexReader indexReader;
    private IndexSearcher indexSearcher;
    private IndexWriter indexWriter;
    private IndexWriterConfig iwConfig;
    @Inject
    private RegionDAO regionDAO;
    @Inject
    private CategoryDAO categoryDAO;
    @Inject
    private ProductDAO productDAO;

    private Map regionMap = new HashMap();
    private Map categoryMap = new HashMap();
    
    public SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    public void init() throws IOException {
        if (initialized) {
            return;
        }
        iwConfig = new IndexWriterConfig(Version.LUCENE_35, new IKAnalyzer());
        directory = SimpleFSDirectory.open(new File(B2B.get("lucene.index.dir")));
        indexWriter = new IndexWriter(directory, iwConfig);
        Document blankDoc = new Document();
        Field f = new Field("test", false, "", Field.Store.YES,
                Field.Index.ANALYZED, Field.TermVector.NO);
        blankDoc.add(f);
        indexWriter.addDocument(blankDoc);
        indexWriter.commit();
        indexReader = IndexReader.open(directory);
        List<Category> categories = categoryDAO.findAll();
        for (Category c : categories) {
            categoryMap.put(c.getId(), c.getName());
        }
        List<Region> regions = regionDAO.findAll();
        for (Region r : regions) {
            regionMap.put(r.getId(), r.getName());
        }
        initialized = true;

    }

    public void updateIndex() throws CorruptIndexException, IOException {
        status = STATUS_INDEXING;
        // add code here;
        init();
        indexWriter.deleteAll();
        List<Product> products = productDAO.findAll();

        Document doc = new Document();
        Field fProductId = new Field("fProductId", false, "", Field.Store.YES,
                Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fProductName = new Field("fProductName", false, "",
                Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fDateCreated = new Field("fDateCreated", false, "",
                Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fRegionId = new Field("fRegionId", false, "", Field.Store.YES,
                Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fCategoryId = new Field("fCategoryId", false, "",
                Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fPrice = new Field("fPrice", false, "", Field.Store.YES,
                Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fIconPic = new Field("fIconPic", false, "", Field.Store.YES,
                Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fPriceUnit = new Field("fPriceUnit", false, "", Field.Store.YES,
                Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fCompanyId = new Field("fCompanyId", false, "", Field.Store.YES,
                Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fCompanyName = new Field("fCompanyName", false, "",
                Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fSummary = new Field("fSummary", false, "", Field.Store.YES,
                Field.Index.ANALYZED, Field.TermVector.NO);
        Field fBigImage = new Field("fBigImage", false, "", Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        Field fPromInd = new Field("fPromInd", false, "", Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO);
        
        for (Product p : products) {
        	if("delete".equals(p.getStatus())){
        		continue;
        	}
            fProductId.setValue(p.getId() + "");
            fProductName.setValue(p.getName());
            fRegionId.setValue(p.getRegionId());
            Date d = p.getDateCreated();
            if(d!=null){
                String sDateCreated = sdf.format(d);
                fDateCreated.setValue(sDateCreated);
            }
            else{
                fDateCreated.setValue("Unknown");
            }
            fCategoryId.setValue(p.getCategoryId());
            fPrice.setValue(p.getPrice() + "");
            fIconPic.setValue(p.getIconImage()==null?"":p.getIconImage());
            fBigImage.setValue(p.getBigImage()==null?"":p.getBigImage());
            fPromInd.setValue(p.getPromoteIndicator()==null?"":p.getPromoteIndicator());
            fPriceUnit.setValue(p.getUnit());
            Member m = p.getMember();
            String memberName = (m == null ? "" : m.getName());
            String memberId = (m == null ? "" : m.getId() + "");
            fCompanyId.setValue(memberId);
            fCompanyName.setValue(memberName);
            String summary = p.getName() + " "
                    + getFullCategoryPath(p.getCategoryId()) + " "
                    + getFullRegionPath(p.getRegionId()) + " " + memberName;
            fSummary.setValue(summary);

            doc.add(fProductId);
            doc.add(fProductName);
            doc.add(fDateCreated);
            doc.add(fRegionId);
            doc.add(fCategoryId);
            doc.add(fPrice);
            doc.add(fIconPic);
            doc.add(fPriceUnit);
            doc.add(fCompanyId);
            doc.add(fCompanyName);
            doc.add(fSummary);
            doc.add(fBigImage);
            doc.add(fPromInd);

            indexWriter.addDocument(doc);
        }
        indexWriter.commit();

        indexReader.close();
        indexReader = IndexReader.open(directory);

        status = STATUS_OK;

    }

    public SearchResult search(SearchCriteria criteria)
            throws IOException {
        init();
        IndexSearcher searcher = new IndexSearcher(indexReader);
        Query regionQuery = null;
        Query categoryQuery = null;
        Query keywordQuery = null;
        
        BooleanQuery query = new BooleanQuery();
        
        if(criteria.getRegionId()!=null&&!"".equals(criteria.getRegionId())){
            Term regionTerm = new Term("fRegionId", criteria.getRegionId());
            regionQuery = new PrefixQuery(regionTerm);
            query.add(regionQuery, BooleanClause.Occur.MUST);
        }
        if(criteria.getCategoryId()!=null&&!"".equals(criteria.getCategoryId())){
            Term categoryTerm = new Term("fCategoryId", criteria.getCategoryId());
            categoryQuery = new PrefixQuery(categoryTerm);
            query.add(categoryQuery, BooleanClause.Occur.MUST);
        }
        if(criteria.getKeyword()!=null&&!"".equals(criteria.getKeyword())){
            keywordQuery = IKQueryParser.parse("fSummary", criteria.getKeyword());
            query.add(keywordQuery, BooleanClause.Occur.MUST);
        }
        int size = criteria.getPage() * criteria.getPageSize();
        int start = (criteria.getPage()-1)*criteria.getPageSize();
        if(size<=0){
            size = 10;
        }
        if(start<=0){
            start = 0;
        }
        int total = criteria.getPageSize();
        if(total<=0){
            total = 10;
        }
        TopScoreDocCollector res = TopScoreDocCollector.create(size, false);
        searcher.search(query, res);
        System.out.println("Total hits: " + res.getTotalHits());
        TopDocs tds = res.topDocs(start, total);
        System.out.println("Total hits: " + tds.totalHits);
        ScoreDoc[] sd = tds.scoreDocs;
        Document doc = null;
        List<Product> products = new ArrayList();
        for (int i = 0; i < sd.length; i++) {
            doc = indexReader.document(sd[i].doc);
            Product p = new Product();
            Field fProductId = (Field) doc.getFieldable("fProductId");
            String sProductId = fProductId.stringValue();
            sProductId = "".equals(sProductId) ? "-1" : sProductId;
            int productId = Integer.valueOf(sProductId);
            Field fProductName = (Field) doc.getFieldable("fProductName");
            String productName = fProductName.stringValue();
            Field fDateCreated = (Field) doc.getFieldable("fDateCreated");
            p.setsDateCreated(fDateCreated.stringValue());
            Field fRegionId = (Field) doc.getFieldable("fRegionId");
            String regionId = fRegionId.stringValue();
            Region region = new Region();
            region.setId(regionId);
            region.setName((String) regionMap.get(regionId));
            Field fCategoryId = (Field) doc.getFieldable("fCategoryId");
            String categoryId = fCategoryId.stringValue();
            Category category = new Category();
            category.setId(categoryId);
            category.setName((String) categoryMap.get(categoryId));
            Field fPrice = (Field) doc.getFieldable("fPrice");
            String sPrice = fPrice.stringValue();
            sPrice = "".equals(sPrice) ? "0" : sPrice;
            //float price = Float.valueOf(sPrice);
            Field fIconPic = (Field) doc.getFieldable("fIconPic");
            String iconPic = fIconPic.stringValue();
            
            Field fBigImage = (Field) doc.getFieldable("fBigImage");
            String bigImageStr = fBigImage.stringValue();
            
            Field fPromoteInd = (Field) doc.getFieldable("fPromInd");
            String promoteIndStr = fPromoteInd.stringValue();
            
            Field fPriceUnit = (Field) doc.getFieldable("fPriceUnit");
            String priceUnit = fPriceUnit.stringValue();
            Field fCompanyId = (Field) doc.getFieldable("fCompanyId");
            String sMemberId = fCompanyId.stringValue();
            sMemberId = "".equals(sMemberId) ? "-1" : sMemberId;
            int memberId = Integer.valueOf(sMemberId);
            Member member = new Member();
            member.setId(memberId);
            Field fCompanyName = (Field) doc.getFieldable("fCompanyName");
            String companyName = fCompanyName.stringValue();
            member.setName(companyName);
            p.setId(productId);
            p.setCategory(category);
            p.setCategoryId(categoryId);
            p.setIconImage(iconPic);
            p.setMember(member);
            p.setName(productName);
            p.setPrice(sPrice);
            p.setRegion(region);
            p.setUnit(priceUnit);
            p.setBigImage(bigImageStr);
            p.setPromoteIndicator(promoteIndStr);

            products.add(p);
        }
        //return products;
        SearchResult sr = new SearchResult();
        sr.setResults(products);
        sr.setTotalHits(tds.totalHits);
        return sr;
    }

    public void deleteIndex() {
        status = STATUS_INDEXING;
        try {
            indexSearcher.close();
            indexWriter.deleteAll();
            indexWriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (indexWriter.isLocked(directory)) {
                    indexWriter.unlock(directory);
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    private String getFullRegionPath(String regionId) {
        if (regionId.length() < 3) {
            return "";
        }
        int k = 1;
        String region = "";
        String id = "";
        while (k * 3 <= regionId.length()) {
            id = regionId.substring(0, k * 3);
            region = region + regionMap.get(id);
            k++;
        }
        return region;
    }

    private String getFullCategoryPath(String categoryId) {
        if (categoryId.length() < 3) {
            return "";
        }
        int k = 1;
        String category = "";
        String id = "";
        while (k * 3 <= categoryId.length()) {
            id = categoryId.substring(0, k * 3);
            category = category + categoryMap.get(id);
            k++;
        }
        return category;
    }

    public RegionDAO getRegionDAO() {
        return regionDAO;
    }

    public void setRegionDAO(RegionDAO regionDAO) {
        this.regionDAO = regionDAO;
    }

    public CategoryDAO getCategoryDAO() {
        return categoryDAO;
    }

    public void setCategoryDAO(CategoryDAO categoryDAO) {
        this.categoryDAO = categoryDAO;
    }

    public ProductDAO getProductDAO() {
        return productDAO;
    }

    public void setProductDAO(ProductDAO productDAO) {
        this.productDAO = productDAO;
    }

    public static void main2(String[] args) throws IOException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        CategoryDAO categoryDAO = (CategoryDAO) ctx.getBean("CategoryDAO");
        RegionDAO regionDAO = (RegionDAO) ctx.getBean("RegionDAO");
        ProductDAO productDAO = (ProductDAO) ctx.getBean("ProductDAO");
        SearchEngine engine = new SearchEngine();
        engine.setCategoryDAO(categoryDAO);
        engine.setProductDAO(productDAO);
        engine.setRegionDAO(regionDAO);
        engine.init();
        //engine.updateIndex();
        BufferedReader r = new BufferedReader(new InputStreamReader(
                new FileInputStream(
                        "f:/workspace/b2b/src/com/sunb2b/test/key.txt")));
        String line = r.readLine();
        
    }
    
    public static void main(String [] args) throws IOException{
        Directory directory = SimpleFSDirectory.open(new File("c:/bb2/index"));
        IndexReader indexReader = IndexReader.open(directory);
        IndexSearcher searcher = new IndexSearcher(indexReader);
        Query regionQuery = null;
        Query categoryQuery = null;
        Query keywordQuery = null;
        BufferedReader r = new BufferedReader(new InputStreamReader(
                new FileInputStream(
                        "C:\\workspace\\spring3\\src\\test\\keyword.txt")));
        String line = r.readLine();
        System.out.println("keyword:" + line);
        
        Term regionTerm = new Term("fRegionId", "100108");
        regionQuery = new PrefixQuery(regionTerm);
        
        Term categoryTerm = new Term("fCategoryId", "100102");
        categoryQuery = new PrefixQuery(categoryTerm);
        
        keywordQuery = IKQueryParser.parse("fSummary", line);
        
        BooleanQuery query = new BooleanQuery();
        
        query.add(regionQuery, BooleanClause.Occur.MUST);
        query.add(categoryQuery, BooleanClause.Occur.MUST);
        query.add(keywordQuery, BooleanClause.Occur.MUST);
        
        System.out.println(query);
        
        TopScoreDocCollector res = TopScoreDocCollector.create(10, false);
        searcher.search(query, res);
        System.out.println("Total hits: " + res.getTotalHits());
    }

}